package com.fh.aspect;

import com.fh.annotation.Excel;
import com.fh.util.ExportUtil;
import com.fh.util.PageData;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.*;

/**
 * @author xuxiaorong
 * 创建时间：2017/10/11 14:57
 **/
@SuppressWarnings("AlibabaClassMustHaveAuthor")
@Aspect
@Component
public class ExcelAspect {
	static Logger logger = LoggerFactory.getLogger(ExcelAspect.class);
	
	
	@Pointcut("@annotation(com.fh.annotation.Excel)")
	public void excelPointCut() {
	
	}
	
	@Around("excelPointCut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		ServletRequestAttributes servletRequestAttributes = ((ServletRequestAttributes)
				RequestContextHolder.getRequestAttributes());
		HttpServletRequest request = servletRequestAttributes.getRequest();
		HttpSession session = request.getSession();
		//获取excel标识
		String key = request.getParameter("key");
		Object result = null;
		if (key != null) {
			session.setAttribute(key, false);
			long beginTime = System.currentTimeMillis();
			//获取签名
			MethodSignature signature = (MethodSignature) point.getSignature();
			Method method = signature.getMethod();
			//请求的方法名
			String className = point.getTarget().getClass().getName();
			String methodName = signature.getName();
			try {
				//执行方法
				result = point.proceed();
			} catch (Throwable ex) {//异常通知
				logger.error(className + "." + methodName + "()：执行异常" + ex.getMessage(), ex);
			}
			//执行时长(毫秒)
			long time = System.currentTimeMillis() - beginTime;
			Excel excelAnnotation = method.getAnnotation(Excel.class);
			logger.info("操作日志信息{" + className + "." + methodName
					+ "()：执行" + excelAnnotation.value() + "耗时" + "" + time + "毫秒}");
			if (result instanceof ModelAndView) {
				//数据
				DataModel dataModel = getDataModel(result, excelAnnotation.value());
				//导出
				outExcel(servletRequestAttributes, dataModel, key);
			} else {
				logger.error("返回结果不是一个视图。");
			}
		} else {
			logger.error("导出excel的key 不存在。");
		}
		return result;
	}
	
	/**
	 * excel导出
	 *
	 * @param servletRequestAttributes
	 * @param dataModel
	 */
	private void outExcel(ServletRequestAttributes servletRequestAttributes, DataModel dataModel, String key) {
		HttpSession session = servletRequestAttributes.getRequest().getSession();
		HttpServletResponse response = servletRequestAttributes.getResponse();
		//SXSSFWorkbook 大批量数据导出
		SXSSFWorkbook workBook = null;
		OutputStream outputStream = null;
		try {
			outputStream = response.getOutputStream();
			ExportUtil exportUtil = new ExportUtil(dataModel.getFileName(), dataModel.getHeads(), dataModel.getDatas());
			session.setAttribute(key, true);
			workBook = exportUtil.export();
			// 如果文件名有中文，必须URL编码
			response.reset();
			response.setContentType("application/vnd.ms-excel");
			response.setHeader("Content-disposition", "attachment; filename=" +
					URLEncoder.encode(dataModel.getFileName() + ".xls", "UTF-8"));
			workBook.write(outputStream);
			outputStream.flush();
		} catch (Exception e) {
			//点击取消
			if (!e.getMessage()
					.contains("java.net.SocketException: Software caused connection abort: socket write error")) {
				logger.error("下载异常1", e);
			}
			
		} finally {
			try {
				workBook.close();
				outputStream.close();
			} catch (IOException e) {
				//点击取消
				if (!e.getMessage()
						.contains("java.net.SocketException: Software caused connection abort: socket write error")) {
					logger.error("下载异常2", e);
				}
			}
		}
	}
	
	public static List<String> asList(Object o) {
		List<String> result = null;
		try {
			result = Arrays.asList(asStrArray(o));
		} catch (Exception e) {
			logger.error("转数据异常asList", e);
		}
		return result;
	}
	
	public static String asString(Object o) {
		String result = null;
		try {
			result = String.valueOf(o);
		} catch (Exception e) {
			logger.error("转数据异常asString", e);
		}
		return result;
	}
	
	public static String[] asStrArray(Object o) {
		String[] result = null;
		try {
			result = asString(o).split(",");
		} catch (Exception e) {
			logger.error("转数据异常asArray", e);
		}
		return result;
	}
	
	private DataModel getDataModel(Object result,String operateName){
		ModelAndView mv = (ModelAndView) result;
		HashMap hashMap = (HashMap) mv.getModelMap().get("hashMap");
		//数据准备
		String titles = asString(hashMap.get("titles"));
		String fields = asString(hashMap.get("fields"));
		
		ArrayList<PageData> data = (ArrayList<PageData>) hashMap.get("data");
		//数据
		return new DataModel(titles, fields, data, operateName);
	}
}

/**
 * 数据对象
 */
class DataModel {
	
	private final Logger logger = LoggerFactory.getLogger(DataModel.class);
	private List<String> titles;
	private List<String[]> dataList;
	private String fileName;
	private String[] heads;
	private List<Object[]> datas;
	
	public DataModel(String titles, String fieldStr, List<PageData> data, String name) {
		List<String> title = ExcelAspect.asList(titles);
		List<String> fields = ExcelAspect.asList(fieldStr);
		this.titles = title;
		this.heads = ExcelAspect.asStrArray(titles);
		List<String[]> datas = new ArrayList<>();
		List<Object[]> dataArr = new ArrayList<>();
		try {
			data.stream().forEach(y -> {
				String[] objectList = new String[fields.size()];
				Object[] objects = new String[fields.size()];
				fields.stream().forEach(x -> {
					objectList[fields.indexOf(x)] = String.valueOf(y.get(x));
					objects[fields.indexOf(x)] = String.valueOf(y.get(x));
				});
				datas.add(objectList);
				dataArr.add(objects);
			});
		} catch (Exception e) {
			logger.error("DataModel异常", e);
		}
		this.dataList = datas;
		this.datas = dataArr;
		this.fileName = name;
	}
	
	public List<String> getTitles() {
		return titles;
	}
	
	public void setTitles(List<String> titles) {
		this.titles = titles;
	}
	
	public List<String[]> getDataList() {
		return dataList;
	}
	
	public void setDataList(List<String[]> dataList) {
		this.dataList = dataList;
	}
	
	
	public String getFileName() {
		return fileName;
	}
	
	public void setFileName(String fileName) {
		this.fileName = fileName;
	}
	
	public String[] getHeads() {
		return heads;
	}
	
	public void setHeads(String[] heads) {
		this.heads = heads;
	}
	
	public List<Object[]> getDatas() {
		return datas;
	}
	
	public void setDatas(List<Object[]> datas) {
		this.datas = datas;
	}
	
	@Override
	public String toString() {
		return "DataModel{" +
				"titles=" + titles +
				", dataList=" + dataList +
				", fileName='" + fileName + '\'' +
				", heads=" + Arrays.toString(heads) +
				", datas=" + datas +
				'}';
	}
}

